package project_2;

import Pair;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Iterator;
import java.util.Set;


public class vSwitch {
	
	// The general listener port for every vSwitch
	private static int listener_port = 4444;
	private static String host = "localhost";
	private static Pair<Integer, Integer> vSwitch_ID;
	
	private static ServerSocketChannel listenerServer;
	private static Selector listenerSelector;
	private static Selector readwriteSelector;
	
	private static ObjectOutputStream outStream;
	private static ObjectInputStream inStream;
	
	
	 
	public static void main(String argv[]) throws Exception{
		
		// Command-line arguments used to set the vSwitch's ID  
		if (argv.length == 2){
			vSwitch_ID = new Pair<Integer, Integer>(Integer.parseInt(argv[0]), Integer.parseInt(argv[1]));
		}
		
		// Create the general non-blocking listener channel
		listenerServer = ServerSocketChannel.open();
		listenerServer.configureBlocking(false);
		listenerServer.socket().bind(new InetSocketAddress(host, listener_port)); // Binds the server channel to the listener port
		
		//outStream = new ObjectOutputStream(Channels.newOutputStream((WritableByteChannel) listenerServer));
		//inStream = new ObjectInputStream(Channels.newInputStream((ReadableByteChannel) listenerServer));
		
		// Create the selector for the listener port and bind it to the channel for listening 
		listenerSelector = Selector.open();
		readwriteSelector = Selector.open();
		
		//listenerServer.register(listenerSelector, listenerServer.validOps());
		listenerServer.register(listenerSelector, SelectionKey.OP_ACCEPT);
		//listenerServer.register(readwriteSelector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
		
		while(true){
			// Wait for the next event
			int selectNum = listenerSelector.select();
			System.out.println(selectNum);
			
			// Once the event occurs, get keys
			Set keys = listenerSelector.selectedKeys();
			Iterator i = keys.iterator();		
			  

		    // For each keys...
			while(i.hasNext()) {
				
			  // Get this most recent key
			  SelectionKey key = (SelectionKey) i.next();			

			  // Remove the current key
			  i.remove();
			  
			  
			  // For the key is due to a ping event
			  if (key.isAcceptable()){
				  System.out.println("Accepting");
				// get client socket channel
			      SocketChannel client = listenerServer.accept();
				  				  
				  
			      // Non Blocking I/O
			      client.configureBlocking(false);
			      // recording to the selector (reading)
			      //client.register(readwriteSelector, SelectionKey.OP_READ);
			      //client.register(readwriteSelector, SelectionKey.OP_WRITE);
			      client.register(listenerSelector, SelectionKey.OP_READ | SelectionKey.OP_READ);
			      
			      Runnable r = new vThreadProcess(readwriteSelector);
			      Thread t = new Thread(r);
			      t.start();
			      
			      continue;				  
			  }
			  
			  // Read in the current data from the connection
			  else if (key.isReadable()){
				  System.out.println("Reading");
				  ByteBuffer buf = ByteBuffer.allocateDirect(25);
				  
				  SocketChannel channel = (SocketChannel) key.channel();
				  try {
						//readwriteSelector.select();
						// Once the event occurs, get keys
						//Set<SelectionKey> keys = readwriteSelector.selectedKeys();
						//Iterator<SelectionKey> i = keys.iterator();		
							  System.out.println("Is Reading");
							  buf.clear();
							  int bytesReadTotal = 0;
							  
							   StringBuilder msg = new StringBuilder();
							   for (;;) {
							    int numBytesRead = channel.read(buf);
							    System.out.println(numBytesRead);
							    if (numBytesRead <= 0){
							    	//Thread.currentThread().sleep(1000);
							    	if (channel.isOpen()){
							    		int bytesWritten = channel.write(ByteBuffer.wrap(new String(Integer.toString(bytesReadTotal)).getBytes()));
							    		System.out.println(bytesWritten);
							    	}
							    	
							    }
							    if (numBytesRead==-1)
							        break;
							    if (numBytesRead>0) {
							        buf.flip();
							        byte[] tempb = new byte[buf.remaining()];
							        buf.get(tempb); 
							        String s = new String(tempb);
							        msg.append(s);
							        bytesReadTotal = bytesReadTotal + numBytesRead;
							        buf.clear();
							    }
							   }
							   channel.close();
							   System.out.println(msg);
						  
						  //readwriteSelector.selectedKeys().clear();
						
						
					} catch (IOException e) {
						e.printStackTrace();
					}
				   
			  }
			  
			  else if (key.isWritable()){
				  System.out.println("Writing");
				  Runnable temp = new vThreadReading((SocketChannel) key.channel());				  
				  Thread t = new Thread(temp);
				  //t.start();
				  

				  
				  
				  
//				  // Create the object with the run() method
//				  Runnable runnable = new BasicThread2();
//
//				  // Create the thread supplying it with the runnable object
//				  Thread thread = new Thread(runnable);
//
//				  // Start the thread
//				  thread.start();

				  
			  }
			}
			
		}
	}
	
	
	public Pair<Integer, Integer> getID(){
		return vSwitch_ID;
	}
	
	public void setID(int x, int y){
		this.vSwitch_ID.setFirst(x);
		this.vSwitch_ID.setSecond(y);
	}	
}

class BasicThread2 implements Runnable {
	
    // This method is called when the thread runs
    public void run() {
    	System.out.println("Yeah!");
    }
}

class vThreadProcess implements Runnable{

	private Selector readwriteSelector;
	ByteBuffer buf = ByteBuffer.allocateDirect(1024);
	
	public vThreadProcess(Selector selector){
		this.readwriteSelector = selector;
	}
	
	@Override
	public void run() {
			try {
				readwriteSelector.select();
				// Once the event occurs, get keys
				Set<SelectionKey> keys = readwriteSelector.selectedKeys();
				Iterator<SelectionKey> i = keys.iterator();		
				  

			    // For each key...
				while(i.hasNext()) {
					
				  // Get this most recent key
				  SelectionKey key = i.next();		
				  
				  if (key.isReadable()){
					  System.out.println("Is Readable");
				  }
				  
				  if (key.isWritable()){
					  System.out.println("Is Writable");
					  SocketChannel channel = (SocketChannel) key.channel();
					  buf.clear();
					  
					   StringBuilder msg = new StringBuilder();
					   for (;;) {
					    int numBytesRead = channel.read(buf);
					    if (numBytesRead==-1)
					        break;
					    if (numBytesRead>0) {
					        buf.flip();
					        byte[] tempb = new byte[buf.remaining()];
					        buf.get(tempb); 
					        String s = new String(tempb);
					        msg.append(s);
					    }
					   }
					   channel.close();
					   System.out.println(msg);

				  }
				  
				  // Remove the current key
				  i.remove();
				  //readwriteSelector.selectedKeys().clear();
				}
				
				
			} catch (IOException e) {
				e.printStackTrace();
			}
		
	}
	
}

class vThreadReading implements Runnable{

	ByteBuffer buf = ByteBuffer.allocateDirect(1024);
	private SocketChannel client;

	public vThreadReading(){		
	}
	
	public vThreadReading(SocketChannel client){
		this.client = client;
	}
	
	@Override
	public void run() {
		buf.clear();
		try{	
			int numBytesRead = client.read(buf);
			System.out.println(0);
			if (numBytesRead == -1){
				client.close();
			}
			else {
				System.out.println(2);
				buf.flip();
				System.out.println(3);
				byte[] tempb = new byte[buf.remaining()];
				System.out.println(4);
			
				buf.get(tempb);	
				
				String s = new String(tempb);
				
				//System.out.println(s);
				System.out.println(tempb.length);
			}
		}
		catch (Exception e){
			e.printStackTrace();
		}
		
	}
	
}

